#include "../include/http_conn.h"
#include "../include/log.h"

#include <fstream>
#include <map>
// #include <mysql-cppconn-8/mysql/jdbc.h>

/* ----------------- 触发标志宏 ------------------- */

// LT 水平触发阻塞
// ET 边缘触发阻塞

#ifdef ET_MODE
#define CONNFD_ET
#define LISTENFD_ET
#elif LT_MODE
#define CONNFD_LT
#define LISTENFD_LT
#else
#define CONNFD_ET
#define LISTENFD_ET
#endif

/* ----------------- 定义HTTP响应状态信息 ------------------- */

const char *OK_200_TITLE = "OK";

const char *ERROR_400_TITLE = "Bad Request\n";
const char *ERROR_400_FORM = "请求中存在语法问题或者服务器无法理解\n";

const char *ERROR_403_TITLE = "Forbidden\n";
const char *ERROR_403_FORM = "请求被服务器拒绝，因为客户端没有访问权限\n";

const char *ERROR_404_TITLE = "Not Found\n";
const char *ERROR_404_FORM = "请求的资源不存在\n";

const char *ERROR_500_TITLE = "Internal Server Error\n";
const char *ERROR_500_FORM = "服务器遇到了未知的错误\n";

/* ----------------- 网站根目录 ------------------- */

const char *DOC_ROOT = "/home/mikelucis/Programming-Sty/HPS/root";

/* ----------------- 辅助函数 ------------------- */

int setNonblocking(int fd)
{
    int old_opt = fcntl(fd, F_GETFL);
    int new_opt = old_opt | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_opt);
    return old_opt;
}

void addFd(int epollfd, int fd, bool one_shot)
{
    epoll_event event;
    event.data.fd = fd;

#ifdef CONNFD_LT
    event.events = EPOLLIN | EPOLLRDHUP;
#endif

#ifdef CONNFD_ET
    event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
#endif

#ifdef LISTENFD_LT
    event.events = EPOLLIN | EPOLLRDHUP;
#endif

#ifdef LISTENFD_ET
    event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
#endif

    if (one_shot)
        event.events |= EPOLLONESHOT;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setNonblocking(fd);
}

void removeFd(int epollfd, int fd)
{
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
    close(fd);
}

void modFd(int epollfd, int fd, int ev)
{
    epoll_event event;
    event.data.fd = fd;

#ifdef CONNFD_ET
    event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
#endif

#ifdef CONNFD_LT
    event.events = ev | EPOLLONESHOT | EPOLLRDHUP;
#endif

    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}

int http_conn::m_user_count = 0;
int http_conn::m_epollfd = -1;

/* ----------------- 操作函数 ------------------- */

void http_conn::init()
{

#ifdef ET_MODE
    fprintf(stdout, "[ET MODE HTTP]\n");
#elif LT_MODE
    fprintf(stdout, "[LT MODE HTTP]\n");
#else
    fprintf(stdout, "[DEFAULT ET MODE HTTP]\n");
#endif

    m_check_state = CHECK_STATE_REQUESTLINE;
    m_linger = false;
    m_method = GET;
    m_url = 0;
    m_version = 0;
    m_content_length = 0;
    m_host = 0;
    m_start_line = 0;
    m_check_idx = 0;
    m_read_idx = 0;
    m_write_idx = 0;
    mysql_conn = nullptr;
    cgi = 0;
    bytes_have_send = 0;
    bytes_to_send = 0;

    memset(m_read_buffer, '\0', READ_BUFFER_SIZE);
    memset(m_write_buffer, '\0', WRITE_BUFFER_SIZE);
    memset(m_read_buffer, '\0', FILENAME_LEN);
}

void http_conn::init(int sockfd, const sockaddr_in &addr)
{
    m_sockfd = sockfd;
    m_address = addr;

    // DEBUG: 阻止TIMEWAIT状态
    // int reuse = 1;
    // setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    addFd(m_epollfd, sockfd, true);
    m_user_count++;
    init();
}

void http_conn::close_conn(bool real_close)
{
    if (real_close && (m_sockfd != -1))
    {
        removeFd(m_epollfd, m_sockfd);
        m_sockfd = -1;
        // 关闭一个连接时，客户总量-1
        m_user_count--;
    }
}

void http_conn::process()
{
    HTTP_CODE read_ret = process_read();
    // 请求不完整,需要继续接受数据
    if (read_ret == NO_REQUEST)
    {
        // 注册并监听读事件
        modFd(m_epollfd, m_sockfd, EPOLLIN);
        return;
    }
    // 完成报文响应
    bool write_ret = process_write(read_ret);
    if (!write_ret)
        close_conn();
    // 注册并监听写事件
    modFd(m_epollfd, m_sockfd, EPOLLOUT);
}

bool http_conn::read()
{
    if (m_read_idx >= READ_BUFFER_SIZE)
        return false;
    int bytes_read = 0;

#ifdef CONNFD_LT
    bytes_read = recv(m_sockfd, m_read_buffer + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0);
    m_read_idx += bytes_read;

    if (bytes_read <= 0)
        return false;
    return true;
#endif

#ifdef CONNFD_ET
    while (1)
    {
        bytes_read = recv(m_sockfd, m_read_buffer + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0);
        if (bytes_read == -1)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                break;
            return false;
        }
        else if (bytes_read == 0)
            return false;
        m_read_idx += bytes_read;
    }
    return true;
#endif
}

bool http_conn::write()
{
    int temp = 0;
    // 报文响应为空
    if (bytes_to_send == 0)
    {
        modFd(m_epollfd, m_sockfd, EPOLLIN);
        init();
        return true;
    }

    while (1)
    {
        temp = writev(m_sockfd, m_iv, m_iv_count);
        // 发送失败
        if (temp <= -1)
        {
            // 若TCP写缓存没有空间，则等待下一轮EPOLLOUT事件
            if (errno == EAGAIN)
            {
                // 重新注册写事件
                modFd(m_epollfd, m_sockfd, EPOLLOUT);
                return true;
            }
            // 发送失败,取消映射
            unmap();
            return false;
        }

        // 更新已经发送/还需发送的字节数
        bytes_have_send += temp;
        bytes_to_send -= temp;

        // 第一个iovec头部信息是否发送完
        if (bytes_have_send >= m_iv[0].iov_len)
        {
            // 已发送完毕,改变iov指针,发送第二个iovec数据
            m_iv[0].iov_len = 0;
            m_iv[1].iov_base = m_file_address + (bytes_have_send - m_write_idx);
            m_iv[1].iov_len = bytes_to_send;
        }
        else
        {
            // 继续发送第一个iovec剩余数据
            m_iv[0].iov_base = m_write_buffer + bytes_have_send;
            m_iv[0].iov_len = m_iv[0].iov_len - bytes_have_send;
        }

        // 判断数据是否完全发送完
        if (bytes_to_send <= 0)
        {
            // 发送HTTP成功, 取消映射
            unmap();
            // 重置EPOLLIN事件
            modFd(m_epollfd, m_sockfd, EPOLLIN);
            // 根据Connection判断是否长连接
            if (m_linger)
            {
                init();
                return true;
            }
            else
                return false;
        }
    }
}

static std::map<std::string, std::string> users;
static locker lock;

void http_conn::initmysql_result(db_connection_pool *connPool)
{
    sql::Connection *conn = nullptr;
    connRAII mysqlCon(&conn, connPool);

    try
    {
        sql::Statement *stmt = conn->createStatement();
        sql::ResultSet *res = stmt->executeQuery("select username, password from user");
        while (res->next())
        {
            std::string usr = res->getString("username");
            std::string psw = res->getString("password");
            users[usr] = psw;
        }
    }
    catch (sql::SQLException &e)
    {
        std::cerr << "Select Account Info Error: " << e.what() << ", MySQL Error Code:" << e.getErrorCode() << std::endl;
        LOG_ERROR("Select Account Info Error: %s, MySQL Error Code: %d\n", e.what(), e.getErrorCode());
    }
}

/* ---------------- HTTP请求分析组函数 ---------------- */

http_conn::HTTP_CODE http_conn::process_read()
{
    LINE_STATUS line_status = LINE_OK;
    HTTP_CODE ret = NO_REQUEST;
    char *text = 0;
    while (((m_check_state == CHECK_STATE_CONTENT) && (line_status == LINE_OK)) || ((line_status = parse_line()) == LINE_OK))
    {
        text = get_line();
        m_start_line = m_check_idx;
        LOG_INFO("%s", text);
        Log::getInstance()->flush();
        printf("Got 1 Http Line: %s\n", text);
        switch (m_check_state)
        {
        case CHECK_STATE_REQUESTLINE:
        {
            ret = parse_request_line(text);
            if (ret == BAD_REQUEST)
                return BAD_REQUEST;
            break;
        }
        case CHECK_STATE_HEADER:
        {
            ret = parse_headers(text);
            if (ret == BAD_REQUEST)
                return BAD_REQUEST;
            else if (ret == GET_REQUEST)
                return do_request();
            break;
        }
        case CHECK_STATE_CONTENT:
        {
            ret = parse_content(text);
            if (ret == GET_REQUEST)
                return do_request();
            line_status = LINE_OPEN;
            break;
        }
        default:
        {
            return INTERNAL_ERROR;
        }
        }
    }
    return NO_REQUEST;
}

http_conn::HTTP_CODE http_conn::parse_request_line(char *text)
{
    m_url = strpbrk(text, " \t");
    if (!m_url)
        return BAD_REQUEST;
    *m_url++ = '\0';
    char *method = text;
    if (strcasecmp(method, "GET") == 0)
        m_method = GET;
    else if (strcasecmp(method, "POST") == 0)
    {
        m_method = POST;
        cgi = 1;
    }
    else
        return BAD_REQUEST;
    m_url += strspn(m_url, " \t");
    m_version = strpbrk(m_url, " \t");
    if (!m_version)
        return BAD_REQUEST;
    *m_version++ = '\0';
    m_version += strspn(m_version, " \t");
    if (strcasecmp(m_version, "HTTP/1.1") != 0)
        return BAD_REQUEST;
    if (strncasecmp(m_url, "http://", 7) == 0)
    {
        m_url += 7;
        m_url = strchr(m_url, '/');
    }
    if (strncasecmp(m_url, "https://", 8) == 0)
    {
        m_url += 8;
        m_url = strchr(m_url, '/');
    }
    if (!m_url || m_url[0] != '/')
        return BAD_REQUEST;
    if (strlen(m_url) == 1)
        strcat(m_url, "index.html");
    m_check_state = CHECK_STATE_HEADER;
    return NO_REQUEST;
}

http_conn::HTTP_CODE http_conn::parse_headers(char *text)
{
    // 头部字段解析完毕
    if (text[0] == '\0')
    {
        // 若HTTP请求有消息体，读取length字节消息体
        if (m_content_length != 0)
        {
            // 进入CHECK CONTENT状态
            m_check_state = CHECK_STATE_CONTENT;
            return NO_REQUEST;
        }
        // 无则表示已经获得完整的HTTP请求
        return GET_REQUEST;
    }
    // 处理相关字段
    else if (strncasecmp(text, "Connection:", 11) == 0)
    {
        text += 11;
        text += strspn(text, " \t");
        if (strcasecmp(text, "keep-alive") == 0)
            m_linger = true;
    }
    else if (strncasecmp(text, "Content-Length:", 15) == 0)
    {
        text += 15;
        text += strspn(text, " \t");
        m_content_length = atol(text);
    }
    else if (strncasecmp(text, "Host:", 5) == 0)
    {
        text += 5;
        text += strspn(text, " \t");
        m_host = text;
    }
    else
    {
        LOG_INFO("Oh! Unknow header: %s", text);
        // printf("Oh! Unknow Header: %s\n", text);
    }
    return NO_REQUEST;
}

http_conn::HTTP_CODE http_conn::parse_content(char *text)
{
    // 判断是否完整被读入
    if (m_read_idx >= (m_content_length + m_check_idx))
    {
        text[m_content_length] = '\0';
        // 用户名及密码
        m_string = text;
        return GET_REQUEST;
    }
    return NO_REQUEST;
}

http_conn::HTTP_CODE http_conn::do_request()
{
    // 当得到完整正确的请求时，服务端开始分析目标文件的属性

    strcpy(m_real_file, DOC_ROOT);
    int len = strlen(DOC_ROOT);
    const char *p = strrchr(m_url, '/');

    // CGI处理
    if (cgi && (*(p + 1) == '2' || *(p + 1) == '3'))
    {
        // UNKOWN
        char flag = m_url[1];

        char *m_url_real = (char *)malloc(sizeof(char) * 200);
        strcpy(m_url_real, "/");
        strcat(m_url_real, m_url + 2);
        strncpy(m_real_file + len, m_url_real, FILENAME_LEN - len - 1);
        free(m_url_real);

        // 提取用户名与密码
        char name[100], psw[100];
        int i;
        for (i = 5; m_string[i] != '&'; ++i)
            name[i - 5] = m_string[i];
        name[i - 5] = '\0';

        int j = 0;
        for (i = i + 10; m_string[i] != '\0'; ++i, ++j)
            psw[j] = m_string[i];
        psw[j] = '\0';

        // 同步线程注册验证
        if (*(p + 1) == '3')
        {
            char *sql_insert = (char *)malloc(sizeof(char) * 200);
            strcpy(sql_insert, "insert into user(username, password) values(?, ?)");
            if (users.find(name) == users.end())
            {
                int line_cnt = 0;
                lock.lock();
                try
                {
                    sql::PreparedStatement *preStem = mysql_conn->prepareStatement(sql_insert);
                    preStem->setString(1, name);
                    preStem->setString(2, psw);
                    line_cnt = preStem->executeUpdate();
                    users.emplace(std::pair<std::string, std::string>(name, psw));
                }
                catch (sql::SQLException &e)
                {
                    std::cerr << "Select Account Info Error: " << e.what() << ", MySQL Error Code:" << e.getErrorCode() << std::endl;
                    LOG_ERROR("Select Account Info Error: %s, MySQL Error Code: %d", e.what(), e.getErrorCode());
                }
                lock.unlock();

                if (line_cnt)
                    strcpy(m_url, "/login.html");
                else
                    strcpy(m_url, "/registerErr.html");
            }
            else
                strcpy(m_url, "/registerErr.html");
        }
        // 同步线程登录验证
        else if (*(p + 1) == '2')
        {
            if (users.find(name) != users.end() && users[name] == psw)
                strcpy(m_url, "/Welcome.html");
            else
                strcpy(m_url, "/loginErr.html");
        }
    }

    if (*(p + 1) == '0')
    {
        char *m_url_real = (char *)malloc(sizeof(char) * 200);
        strcpy(m_url_real, "/register.html");
        strncpy(m_real_file + len, m_url_real, strlen(m_url_real));
        free(m_url_real);
    }
    else if (*(p + 1) == '1')
    {
        char *m_url_real = (char *)malloc(sizeof(char) * 200);
        strcpy(m_url_real, "/login.html");
        strncpy(m_real_file + len, m_url_real, strlen(m_url_real));
        free(m_url_real);
    }
    else if (*(p + 1) == '5')
    {
        char *m_url_real = (char *)malloc(sizeof(char) * 200);
        strcpy(m_url_real, "/picture.html");
        strncpy(m_real_file + len, m_url_real, strlen(m_url_real));
        free(m_url_real);
    }
    else if (*(p + 1) == '6')
    {
        char *m_url_real = (char *)malloc(sizeof(char) * 200);
        strcpy(m_url_real, "/video.html");
        strncpy(m_real_file + len, m_url_real, strlen(m_url_real));
        free(m_url_real);
    }
    else
        strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1);

    // 目标文件是否存在
    if (stat(m_real_file, &m_file_stat) < 0)
        return NO_RESOURCE;
    // 目标文件是否可读
    if (!(m_file_stat.st_mode & S_IROTH))
        return FORBIDDEN_REQUEST;
    // 目标文件是否为目录
    if (S_ISDIR(m_file_stat.st_mode))
        return BAD_REQUEST;
    // 打开文件并将其映射到内存
    int fd = open(m_real_file, O_RDONLY);
    m_file_address = (char *)mmap(0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    close(fd);
    return FILE_REQUEST;
}

http_conn::LINE_STATUS http_conn::parse_line()
{
    char tmp;
    for (; m_check_idx < m_read_idx; ++m_check_idx)
    {
        tmp = m_read_buffer[m_check_idx];
        if (tmp == '\r')
        {
            if ((m_check_idx + 1) == m_read_idx)
                return LINE_OPEN;
            else if (m_read_buffer[m_check_idx + 1] == '\n')
            {
                m_read_buffer[m_check_idx++] = '\0';
                m_read_buffer[m_check_idx++] = '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        }
        else if (tmp == '\n')
        {
            if ((m_check_idx > 1) && (m_read_buffer[m_check_idx - 1] == '\r'))
            {
                m_read_buffer[m_check_idx - 1] = '\0';
                m_read_buffer[m_check_idx++] = '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        }
    }
    return LINE_OPEN;
}

/* ---------------- HTTP响应处理组函数 ---------------- */

bool http_conn::process_write(HTTP_CODE ret)
{
    // 根据服务器返回结果，决定返回客户端的内容
    switch (ret)
    {
    case INTERNAL_ERROR:
    {
        add_status_line(500, ERROR_500_TITLE);
        add_headers(strlen(ERROR_500_FORM));
        if (!add_content(ERROR_500_FORM))
            return false;
        break;
    }
    case BAD_REQUEST:
    {
        add_status_line(400, ERROR_400_TITLE);
        add_headers(strlen(ERROR_400_FORM));
        if (!add_content(ERROR_400_FORM))
            return false;
        break;
    }
    case NO_RESOURCE:
    {
        add_status_line(404, ERROR_404_TITLE);
        add_headers(strlen(ERROR_404_FORM));
        if (!add_content(ERROR_404_FORM))
            return false;
        break;
    }
    case FORBIDDEN_REQUEST:
    {
        add_status_line(403, ERROR_403_TITLE);
        add_headers(strlen(ERROR_403_FORM));
        if (!add_content(ERROR_403_FORM))
            return false;
        break;
    }
    case FILE_REQUEST:
    {
        add_status_line(200, OK_200_TITLE);
        if (m_file_stat.st_size != 0)
        {
            add_headers(m_file_stat.st_size);
            m_iv[0].iov_base = m_write_buffer;
            m_iv[0].iov_len = m_write_idx;
            m_iv[1].iov_base = m_file_address;
            m_iv[1].iov_len = m_file_stat.st_size;
            m_iv_count = 2;
            bytes_to_send = m_write_idx + m_file_stat.st_size;
            return true;
        }
        else
        {
            const char *ok_string = "<html><body><h2>INFO PAGE</h2></body></html>";
            add_headers(strlen(ok_string));
            if (!add_content(ok_string))
                return false;
        }
    }
    default:
        return false;
    }

    LOG_INFO("Request: \n%s", m_write_buffer);
    Log::getInstance()->flush();

    m_iv[0].iov_base = m_write_buffer;
    m_iv[0].iov_len = m_write_idx;
    m_iv_count = 1;
    bytes_to_send = m_write_idx;
    return true;
}

void http_conn::unmap()
{
    if (m_file_address)
    {
        munmap(m_file_address, m_file_stat.st_size);
        m_file_address = 0;
    }
}

bool http_conn::add_response(const char *format, ...)
{
    if (m_write_idx >= WRITE_BUFFER_SIZE)
        return false;
    va_list arg_list;
    va_start(arg_list, format);
    int len = vsnprintf(m_write_buffer + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list);
    if (len >= (WRITE_BUFFER_SIZE - 1 - m_write_idx))
    {
        va_end(arg_list);
        return false;
    }
    m_write_idx += len;
    va_end(arg_list);
    return true;
}

bool http_conn::add_content(const char *content)
{
    return add_response("%s", content);
}

bool http_conn::add_status_line(int status, const char *title)
{
    return add_response("%s %d %s\r\n", "HTTP/1.1", status, title);
}

bool http_conn::add_headers(int content_len)
{
    return add_content_length(content_len) | add_linger() | add_blank_line();
}

bool http_conn::add_content_length(int content_len)
{
    return add_response("Content-Length:%d\r\n", content_len);
}

bool http_conn::add_content_type()
{
    return add_response("Content-Type:%s\r\n", "text/html");
}

bool http_conn::add_linger()
{
    return add_response("Connection:%s\r\n", m_linger ? "keep-alive" : "close");
}

bool http_conn::add_blank_line()
{
    return add_response("%s", "\r\n");
}
